package com.cxy.common.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.Ordered;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/*********************************************************
 * 文件名称：ShutDownHookCache.java
 * 系统名称：CXY自定义系统
 * 模块名称：com.cxy.common.util
 * 功能说明：程序关闭钩子
 * 开发人员 @author：caoxy31978
 * 开发时间 @date：2023/6/28 15:36
 * 修改记录：程序版本  修改日期  修改人员  修改单号  修改说明
 *********************************************************/
@Slf4j
public class ShutDownHookCache implements SpringApplicationRunListener, Ordered {
    private static Map<String, ShutDownProcess> shutdownHooks = new ConcurrentHashMap();
    private static Map<String, Thread> shutdownThreads = new ConcurrentHashMap();
    private static final AtomicBoolean IS_REGISTERED = new AtomicBoolean(false);
    private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener();

    public ShutDownHookCache(SpringApplication application, String[] args) {
    }

    public static void register(String key, final ShutDownProcess shutDownProcess) {
        try {
            if (!shutdownHooks.containsKey(key)) {
                shutdownHooks.put(key, shutDownProcess);
                Thread thread = new Thread(() -> {
                    try {
                        shutDownProcess.doShutDown();
                    } catch (Throwable e) {
                        log.warn(e.getMessage());
                    }
                });
                shutdownThreads.put(key, thread);
                Runtime.getRuntime().addShutdownHook(thread);
            }
        } catch (Throwable e) {
            log.debug(e.getMessage());
        }

    }

    public static void unRegisterFromJvm() {
        shutdownThreads.values().forEach((it) -> {
            try {
                Runtime.getRuntime().removeShutdownHook(it);
            } catch (Throwable e) {
                log.warn(e.getMessage());
            }
        });
    }

    public int getOrder() {
        return Integer.MAX_VALUE;
    }

    @Override
    public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
        if (!IS_REGISTERED.get() && IS_REGISTERED.compareAndSet(false, true)) {
            try {
                context.addApplicationListener(SHUTDOWN_HOOK_LISTENER);
                unRegisterFromJvm();
            } catch (Throwable e) {
                log.warn(e.getMessage());
            }
        }
    }

    private static class ShutdownHookListener implements ApplicationListener, Ordered {
        private AtomicBoolean isUnregistered;

        private ShutdownHookListener() {
            this.isUnregistered = new AtomicBoolean(false);
        }

        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextClosedEvent && !this.isUnregistered.get() && this.isUnregistered.compareAndSet(false, true)) {
                ShutDownHookCache.shutdownHooks.forEach((it, value) -> {
                    try {
                        value.doShutDown();
                    } catch (Throwable e) {
                        log.warn(e.getMessage());
                    }
                });
            }
        }

        public int getOrder() {
            return Integer.MAX_VALUE;
        }
    }
}
